﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.WebTesting;
using System.Diagnostics;

namespace InfoPathExtractionRules
{
    public class IpfsWebTestAutoFixer
    {
        public string InvocationPath;
        private const string FormServerPath = IPExtractionUtil.DefaultFormServerPath;
        private const string PartialPostbackPath = IPExtractionUtil.PartialPostbackPath;
        private int postbackCounter = -1;

        public IpfsWebTestAutoFixer()
        {
            InvocationPath = FormServerPath;
        }

        public void fix(string originalWebTestPath, string fixedWebTestPath)
        {
            DeclarativeWebTest originalWebTest = DeclarativeWebTestSerializer.Open(originalWebTestPath);
            DeclarativeWebTest fixedWebTest = fix(originalWebTest);
            DeclarativeWebTestSerializer.Save(fixedWebTest, fixedWebTestPath);
        }

        public DeclarativeWebTest fix(DeclarativeWebTest dwt)
        {
            WebTestItemCollection items = dwt.Items;

            int invocationIndex = 0;
            WebTestRequest req = null;
            bool foundInvocation = false;
            // Find the invocation request and fix it up
            for (; invocationIndex < items.Count; invocationIndex++)
            {
                req = items[invocationIndex] as WebTestRequest;
                if (req != null)
                {
                    if (isInvocation(req))
                    {
                        foundInvocation = true;
                        autoFixInvocation(req);
                        break;
                    }
                }
            }

            if (!foundInvocation)
            {
                Trace.TraceWarning("Could not find the Invocation request in this webtest");
                invocationIndex = -1;
            }

            int index = invocationIndex + 1;
            postbackCounter = 0;
            // Find each postback request and fix it up
            for (; index < items.Count; index++)
            {
                req = items[index] as WebTestRequest;
                if (req != null)
                {
                    if (isPartialPostback(req))
                    {
                        autoFixPartialPostback(req);
                        postbackCounter++;
                    }
                    else if (isFullPagePostback(req))
                    {
                        autoFixFullPagePostback(req);
                        postbackCounter++;
                    }
                }
            }

            return dwt;
        }

        public bool isInvocation(WebTestRequest req)
        {
            // Check if the request is using the GET transport method
            // and that the url is for an invocation
            return (req.Method == "GET") && (req.Url.Contains(InvocationPath));
        }

        public bool isPartialPostback(WebTestRequest req)
        {
            // Check if the request is using the POST transport method
            // and that the url is for an postback
            return (req.Method == "POST") && (req.Url.Contains(PartialPostbackPath));
        }

        public bool isFullPagePostback(WebTestRequest req)
        {
            // Check if the request is using the POST transport method
            // and that the url is for an invocation
            return (req.Method == "POST") && (req.Url.Contains(InvocationPath));
        }

        public void autoFixInvocation(WebTestRequest req)
        {
            addExtractionRule(req, typeof(ExtractEditingSessionId), "editingSessionId");
            addExtractionRule(req, typeof(ExtractSolutionId), "solutionId");
            addExtractionRule(req, typeof(ExtractInitialCanary), "canary");
        }

        public void addExtractionRule(WebTestRequest req, Type rule, string contextParameterName)
        {
            ExtractionRuleReference ruleReference = new ExtractionRuleReference(rule);
            ruleReference.ContextParameterName = contextParameterName;
            req.ExtractionRuleReferences.Add(ruleReference);
        }

        private void autoFixEventLog(EventLog eventLog)
        {
            EventLog.EventLogInfo eventLogInfo = eventLog.eventLogInfo;

            if (eventLogInfo.PostbackCounter != postbackCounter)
            {
                string error = string.Format("A postback counter is out of sequence.  Got {0} but expected {1}",
                    eventLogInfo.PostbackCounter,
                    postbackCounter);
                Trace.TraceWarning(error);
            }

            // Substitute context parameters in the event log postback body
            eventLogInfo.EditingSessionId = contextParameter("editingSessionId");
            eventLogInfo.SolutionId = contextParameter("solutionId");
            eventLogInfo.Canary = contextParameter("canary");
        }

        public void autoFixPartialPostback(WebTestRequest req)
        {
            StringHttpBody requestHttpBody = null;
            requestHttpBody = req.Body as StringHttpBody;

            if (requestHttpBody != null)
            {
                EventLog eventLog = new EventLog(requestHttpBody.BodyString);
                autoFixEventLog(eventLog);
                requestHttpBody.BodyString = eventLog.ToString();
            }
            else
            {
                Trace.TraceWarning("Did not find string http body in partial postback.");
            }

            // Add Extraction Rule
            addExtractionRule(req, typeof(ExtractPostBackCanary), "canary");
        }

        public void autoFixFullPagePostback(WebTestRequest req)
        {
            FormPostHttpBody body = null;

            body = req.Body as FormPostHttpBody;
            if (body != null)
            {
                FormPostParameterCollection formPostParams = body.FormPostParameters;
                if (formPostParams.Count < 1)
                {
                    Trace.TraceError("Did not find any form post parameters for full page postback.");
                }

                // Find the parameter for the event log and fix it up
                foreach (FormPostParameter param in formPostParams)
                {
                    if (param.Name == IPExtractionUtil.EventLogFormPostbackParameterName)
                    {
                        EventLog eventLog = new EventLog(param.Value);
                        autoFixEventLog(eventLog);
                        param.Value = eventLog.ToString();
                    }
                }
            }
            else
            {
                Trace.TraceWarning("Did not find form post http body in full page postback.");
            }

            // Add Extraction Rule
            addExtractionRule(req, typeof(ExtractPostBackCanary), "canary");
        }

        private static string contextParameter(string str)
        {
            return "{{" + str + "}}";
        }
    }
}
